package com.xiaoshuidi.cloud.module.report.xxljob;

import com.alibaba.nacos.common.utils.CollectionUtils;
import com.xiaoshuidi.cloud.framework.tenant.core.aop.TenantIgnore;
import com.xiaoshuidi.cloud.module.report.dal.DO.ContractHouseOwnerDO;
import com.xiaoshuidi.cloud.module.report.dal.DO.ContractHouseOwnerRoomRefDO;
import com.xiaoshuidi.cloud.module.report.dal.DO.RoomHouseDO;
import com.xiaoshuidi.cloud.module.report.dal.DO.report.OwnerIncomeDetailDayReportDO;
import com.xiaoshuidi.cloud.module.report.dal.DO.report.RentFreePeriodDTO;
import com.xiaoshuidi.cloud.module.report.dal.DO.report.ReportOwnerContractDayRentDO;
import com.xiaoshuidi.cloud.module.report.dal.mapper.ContractHouseOwnerMapper;
import com.xiaoshuidi.cloud.module.report.dal.mapper.ContractHouseOwnerRoomRefMapper;
import com.xiaoshuidi.cloud.module.report.dal.mapper.RoomHouseMapper;
import com.xiaoshuidi.cloud.module.report.dal.mapper.report.OwnerIncomeDetailDayReportMapper;
import com.xiaoshuidi.cloud.module.report.service.contract.ContractOperateService;
import com.xiaoshuidi.cloud.module.report.service.finance.OwnerIncomeDetailReportService;
import com.xiaoshuidi.cloud.module.report.utils.DateUtils;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 业主收入汇总明细job每天凌晨5:30跑一次
 * 将昨天的合同账单数据进行明细处理  处理  统计到天 明细
 */
@Component
@Slf4j
public class OwnerIncomeDetailDayHandleJob {
    //流入
    private static  final DateTimeFormatter formatter =DateTimeFormatter.ofPattern("yyyyMMdd");

    @Resource
    private ContractHouseOwnerMapper contractHouseOwnerMapper;
    @Resource
    private ContractHouseOwnerRoomRefMapper contractHouseOwnerRoomRefMapper;
    @Resource
    private ContractOperateService contractOperateService;
    @Resource
    private OwnerIncomeDetailReportService ownerIncomeDetailReportService;
    @Resource
    private OwnerIncomeDetailDayReportMapper ownerIncomeDetailDayReportMapper;
    @Resource
    private RoomHouseMapper roomHouseMapper;

    @XxlJob("ownerIncomeDetailDayHandleJob")
    @TenantIgnore
    public void execute() {
        DateTimeFormatter df =DateTimeFormatter.ofPattern("yyyy-MM-dd");
        DateTimeFormatter monthFormat =DateTimeFormatter.ofPattern("yyyy年MM月");
        String params = XxlJobHelper.getJobParam();
        LocalDate yesterday = LocalDate.now().minusDays(1);
        String statisMonth = yesterday.format(monthFormat);
        if(StringUtils.isNotBlank(params)){
            try {
                yesterday = LocalDate.parse(params, df);
                statisMonth = yesterday.format(monthFormat);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }
        //获取昨天存在权责的业主合同  业主合同目前较少可以一次处理
        List<ContractHouseOwnerDO> contractHouseOwnerDOS = contractHouseOwnerMapper.selectActiveByDate(yesterday);
        handlerContract(contractHouseOwnerDOS,yesterday,statisMonth);
    }

    /**
     * 单个合同执行流程
     */
    void handlerContract(List<ContractHouseOwnerDO> ownerDOList,LocalDate yesterday,String statisMonth) {
        DateTimeFormatter dateFormat =DateTimeFormatter.ofPattern("yyyy-MM-dd");
//        List<Long> ownerContractIds = ownerDOList.stream().map(ContractHouseOwnerDO::getId).collect(Collectors.toList());
//        List<RoomHouseDO> houseList = roomHouseMapper.selectByIds(roomIds);
        for (ContractHouseOwnerDO ownerDO : ownerDOList) {
            OwnerIncomeDetailDayReportDO detailDO = new OwnerIncomeDetailDayReportDO();
            detailDO.setContractId(ownerDO.getId());
            detailDO.setContractNo(ownerDO.getContractNo());
            detailDO.setOwnerName(ownerDO.getOwnerName());
            detailDO.setOwnerPhone(ownerDO.getPhone());


            List<ContractHouseOwnerRoomRefDO> ownerRoomRefDOS = contractHouseOwnerRoomRefMapper.selectByContractId(ownerDO.getId());
            if(CollectionUtils.isEmpty(ownerRoomRefDOS)){
                continue;
            }
            //拼凑房源名称
            RoomHouseDO houseDO = roomHouseMapper.selectById(ownerRoomRefDOS.get(0).getRoomId());
            if (houseDO == null) {
                log.error("====脏数据 此业主合同id:{}无房间信息",ownerDO.getId());
                continue;
            }
            detailDO.setRoomId(houseDO.getId());
            detailDO.setArea(houseDO.getArea());
            detailDO.setApartmentId(houseDO.getApartmentId());
            detailDO.setStatisticsMonth(statisMonth);
            detailDO.setRoomName(houseDO.getName());
            //集中式和分散式拼凑方法不一样
            StringBuilder houseName = new StringBuilder(houseDO.getArea()).append(houseDO.getBuildingNo()).append("栋");
            if (houseDO.getIsConcentrated()) {
                //集中式 小区名-楼栋-房源名称   room_house.area - room_house.building_no栋 - room_house.name
                detailDO.setHouseResource(houseName.append(houseDO.getName()).toString());
            } else {
                //分散式  小区名-楼栋-单元-房源名称 room_house.area - room_house.building_no栋 - room_house.unit_no单元 - room_house.name
                detailDO.setHouseResource(houseName.append(houseDO.getUnitNo()).append("单元").append(houseDO.getName()).toString());
            }
            detailDO.setMonthRent(ownerDO.getMonthRent());
            detailDO.setContractPeriod(dateFormat.format(ownerDO.getStartTime()) + "~" + dateFormat.format(ownerDO.getEndTime()));
            //计算合同对应的租金
            Map<String, String> amountMap = handlerRent(ownerDO, yesterday);
            detailDO.setCurRentIn(new BigDecimal(amountMap.get("dayRent")));
            detailDO.setCurRentDisc(new BigDecimal(amountMap.get("dayDiscount")));
            detailDO.setCurRentReduce(detailDO.getCurRentIn().subtract(detailDO.getCurRentDisc()));
            //查询合同最近一个月的累计
            //成本到天不需要统计合计金额
//            OwnerIncomeDetailReportDO recentIncomeReport = ownerIncomeDetailReportService.getRecentIncomeReport(ownerDO.getId(), statisMonth);
//            if (recentIncomeReport != null) {
//                detailDO.setAccRentIn(detailDO.getCurRentIn().add(recentIncomeReport.getAccRentIn()));
//                detailDO.setAccRentDisc(detailDO.getCurRentDisc());
//                detailDO.setAccRentReduce(detailDO.getCurRentReduce().add(recentIncomeReport.getAccRentReduce()));
//                detailDO.setAccOtherIn(detailDO.getCurOtherIn().add(detailDO.getAccOtherIn()));
//            } else {
//                detailDO.setAccRentIn(detailDO.getCurRentIn());
//                detailDO.setAccRentDisc(detailDO.getCurRentDisc());
//                detailDO.setAccRentReduce(detailDO.getCurRentReduce());
//                detailDO.setAccOtherIn(detailDO.getCurOtherIn());
//            }
            detailDO.setTenantId(ownerDO.getTenantId());
            detailDO.setDeptId(ownerDO.getDeptId());
            //TODO 如果此合同之前没有权责，需判断是否是补录合同， 补录合同需把历史权责记录在昨天
            handleRecordIngContract(detailDO,ownerDO,yesterday);
            ownerIncomeDetailDayReportMapper.insert(detailDO);
        }
    }

    /**
     * 处理补录合同的权责
     */
    private void handleRecordIngContract(OwnerIncomeDetailDayReportDO detailDO,ContractHouseOwnerDO ownerDO,LocalDate yesterDay){
        List<OwnerIncomeDetailDayReportDO> incomeDetailDayReportDOS = ownerIncomeDetailDayReportMapper.selectByContractId(ownerDO.getId());
        LocalDate towDaysAgo = yesterDay.minusDays(1);
        if(CollectionUtils.isEmpty(incomeDetailDayReportDOS)){
            if(!ownerDO.getStartTime().isAfter(towDaysAgo)){
                //合同第一次计算就需要算之前的成本，如果计算时前天合同还未开始，则不需要计算，否则第一次都需要计算
                int dayOfMonth = towDaysAgo.getDayOfMonth();
                int dayLength = 0;
                DateUtils.MonthAndRemainder monthAndRemainder = DateUtils.monthsAndRemainder(ownerDO.getStartTime(), towDaysAgo);
                //如果前天是账单的上半部分，则安装前天对应的上月月租金进行计算
                int day = ownerDO.getStartTime().minusMonths(-monthAndRemainder.getMonth()).minusDays(1).getDayOfMonth();
                if(day>=dayOfMonth&&ownerDO.getStartTime().getDayOfMonth()!=1){
                    dayLength = towDaysAgo.minusMonths(1).lengthOfMonth();
                }else {
                    dayLength = towDaysAgo.lengthOfMonth();
                }
                ReportOwnerContractDayRentDO reportContractDayRentDO = contractOperateService.selectOwnerByContractIdAndDays(ownerDO.getId(), dayLength);
                BigDecimal dayRent = reportContractDayRentDO.getNormalDayRent().multiply(BigDecimal.valueOf(monthAndRemainder.getRemainder()));
                BigDecimal totalMonthRent = dayRent.add(BigDecimal.valueOf(monthAndRemainder.getMonth()).multiply(ownerDO.getMonthRent()));
                detailDO.setCurRentIn(detailDO.getCurRentIn().add(totalMonthRent));
                //计算租金折扣
                /**
                 * 1、前天正好在免租期时间内  需分段计算
                 * 2、前天不在免租期时间内  直接计算免除租金月份个数*月租金
                 */
                String rentFreePeriod = reportContractDayRentDO.getRentFreePeriod();
                BigDecimal discount = BigDecimal.ZERO;
                if(StringUtils.isNotBlank(rentFreePeriod)){
                    for (String s : rentFreePeriod.split(",")) {
                        String[] split = s.split("~");
                        LocalDate start = LocalDate.parse(split[0], formatter);
                        LocalDate end = LocalDate.parse(split[1], formatter);
                        if(!end.isAfter(towDaysAgo)){
                            //存在2月情况 月差需考虑
                            int monthSpace = DateUtils.getMonthSpace(start, end.minusDays(-2));
                            discount = discount.add(reportContractDayRentDO.getMonthRent().multiply(BigDecimal.valueOf(monthSpace)));
                        }else if(!start.isAfter(towDaysAgo)&&!end.isBefore(towDaysAgo)){
                            //获取月所在天数，计算其日租金
                            DateUtils.MonthAndRemainder remainder = DateUtils.monthsAndRemainder(start, towDaysAgo);
                            int dayOfMonth1 = start.minusMonths(-remainder.getMonth()).lengthOfMonth();
                            ReportOwnerContractDayRentDO rentDO = contractOperateService.selectOwnerByContractIdAndDays(ownerDO.getId(), dayOfMonth1);
                            BigDecimal monthDiscount = rentDO.getMonthRent().multiply(BigDecimal.valueOf(remainder.getMonth()))
                                    .add(rentDO.getNormalDayRent().multiply(BigDecimal.valueOf(remainder.getRemainder())));
                            discount = discount.add(monthDiscount);
                        }
                    }
                }
                detailDO.setCurRentDisc(detailDO.getCurRentDisc().add(discount));
                detailDO.setCurRentReduce(detailDO.getCurRentIn().subtract(detailDO.getCurRentDisc()));
                detailDO.setAccRentIn(detailDO.getCurRentIn());
                detailDO.setAccRentDisc(detailDO.getCurRentDisc());
                detailDO.setAccRentReduce(detailDO.getCurRentReduce());
            }
        }
    }

    /**
     * 计算租金 假如月租金1000
     * 按日租金算  3.15-4.14 分两段  3.1-3.15,3.16-3.31,4.1-4.15,4.16-4.30 且每个月上半段按上月的租金/上月的天数
     * 如4.1-4.15 日租金是 按3月天数31天，1000/31是4.1-4.15的日租金
     * 退租需注意特殊场景 例如4.25-5.24 可能5.27退租也可能6.3退租 此时的延期退租金额计算方式不一样
     *
     * @param ownerDO
     */
    public Map<String, String> handlerRent(ContractHouseOwnerDO ownerDO, LocalDate yesterday) {
        //保存日租金及周期优惠 方便其他需要时直接获取
        List<ReportOwnerContractDayRentDO> dayRentDOS = processDayRent(ownerDO);
        //昨天所在月份拥有的天数
        int pastDays = yesterday.lengthOfMonth();
        //获取昨天所在的天数
        int pastDayNum = yesterday.getDayOfMonth();
        //获取昨天对应的上月所在天数
        int lastMonthDays = yesterday.minusMonths(1).lengthOfMonth();
        //昨天与合同开始日相差多少个月
        DateUtils.MonthAndRemainder monthAndRemainder = DateUtils.monthsAndRemainder(ownerDO.getStartTime(), yesterday);
        //计算昨天所在的月份对应的 结束日期
        int monthSub = monthAndRemainder.getMonth();
        int endDays = ownerDO.getStartTime().plusMonths(monthSub).plusDays(-1).getDayOfMonth();
        //a.1 获取日租金
        BigDecimal dayRent = BigDecimal.ZERO;
        BigDecimal dayDiscount = BigDecimal.ZERO;
        ReportOwnerContractDayRentDO yesterdayMonth = new ReportOwnerContractDayRentDO();
        ReportOwnerContractDayRentDO lastDayMonth = new ReportOwnerContractDayRentDO();
        for (ReportOwnerContractDayRentDO dayRentDO : dayRentDOS) {
            if (pastDays == dayRentDO.getMonthDays()) {
                yesterdayMonth = dayRentDO;
            }
            if (lastMonthDays == dayRentDO.getMonthDays()) {
                lastDayMonth = dayRentDO;
            }
        }
        if (yesterday.isBefore(ownerDO.getEndTime())) {
            //正常房租
            if (pastDayNum < endDays) {
                if(ownerDO.getStartTime().getDayOfMonth()==1){
                    dayRent = yesterdayMonth.getNormalDayRent();
                }else {
                    dayRent = lastDayMonth.getNormalDayRent();
                }
            } else if (pastDayNum == endDays) {
                if (ownerDO.getStartTime().getDayOfMonth() == 1) {
                    //如果合同开始日是1号，则昨天所在的日租金是按当月的算
                    dayRent = yesterdayMonth.getDayRentBalance();
                } else {
                    dayRent = lastDayMonth.getDayRentBalance();
                }
            } else {
                dayRent = yesterdayMonth.getNormalDayRent();
            }
        } else if (yesterday.isEqual(ownerDO.getEndTime())) {
            if (ownerDO.getStartTime().getDayOfMonth() == 1) {
                //如果合同开始日是1号，则昨天所在的日租金是按当月的算
                dayRent = yesterdayMonth.getDayRentBalance();
            } else {
                dayRent = lastDayMonth.getDayRentBalance();
            }
        }
        String rentFreePeriod = yesterdayMonth.getRentFreePeriod();
        if (StringUtils.isNotBlank(rentFreePeriod)) {
            for (String s : rentFreePeriod.split(",")) {
                String[] split = s.split("~");
                LocalDate start = LocalDate.parse(split[0], formatter);
                LocalDate end = LocalDate.parse(split[1], formatter);
                if (!start.isAfter(yesterday) && !end.isBefore(yesterday)) {
                    dayDiscount = dayRent;
                    break;
                }
            }
        }
        Map<String, String> amountMap = new HashedMap();
        //日租金 日折扣, 统计天数
        amountMap.put("dayRent", String.valueOf(dayRent));
        amountMap.put("dayDiscount", String.valueOf(dayDiscount));
        return amountMap;
    }


    /**
     * 保存/获取日租金及优惠
     */
    private List<ReportOwnerContractDayRentDO> processDayRent(ContractHouseOwnerDO ownerDO) {
        //计算免租期
        List<ReportOwnerContractDayRentDO> dayRentList = contractOperateService.selectOwnerListByContractId(ownerDO.getId());
        if (CollectionUtils.isNotEmpty(dayRentList)) {
            return dayRentList;
        }
        LocalDate startTime = ownerDO.getStartTime();
        StringBuilder rentFreePeriod =new StringBuilder();
        String rentFree=null;
        if(CollectionUtils.isNotEmpty( ownerDO.getRentFreePeriod())){
            for (RentFreePeriodDTO periodDTO :  ownerDO.getRentFreePeriod()) {
                if(periodDTO.getFreePeriodMonthNum()!=0){
                    LocalDate localDate1 = startTime.minusYears(-periodDTO.getYearNo()+1);
                    LocalDate localDate2 =  startTime.minusYears(-periodDTO.getYearNo()+1).minusMonths(-periodDTO.getFreePeriodMonthNum()).minusDays(1);
                    rentFreePeriod.append(localDate1.format(formatter)).append("~").append(localDate2.format(formatter)).append(",");
                }

            }
            if(StringUtils.isNotBlank(rentFreePeriod)){
                rentFree= rentFreePeriod.substring(0, rentFreePeriod.length() - 1);
            }
        }
        //计算合同日租金
        List<ReportOwnerContractDayRentDO> list = new ArrayList<>(4);
        for (int i = 28; i <= 31; i++) {
            ReportOwnerContractDayRentDO dayRentDO = new ReportOwnerContractDayRentDO();
            dayRentDO.setContractId(ownerDO.getId());
            dayRentDO.setMonthRent(ownerDO.getMonthRent());
            dayRentDO.setMonthDays(i);
            dayRentDO.setTenantId(ownerDO.getTenantId());
            BigDecimal normalDayRent = ownerDO.getMonthRent().divide(BigDecimal.valueOf(i), 2, RoundingMode.HALF_UP);
            BigDecimal dayRentBalance = ownerDO.getMonthRent().subtract(normalDayRent.multiply(BigDecimal.valueOf(i - 1)));
            dayRentDO.setNormalDayRent(normalDayRent);
            dayRentDO.setDayRentBalance(dayRentBalance);
            dayRentDO.setRentFreePeriod(rentFree);
            list.add(dayRentDO);
        }
        contractOperateService.batchOwnerSaveContractDayRent(list);
        return list;
    }
}
